package sn.layers;

import java.util.ArrayList;
import java.util.List;

import sn.Neuron;
import sn.functions.ActivationFunction;

public abstract class Layer {
	protected List<Neuron> neurons;
	protected Layer nextLayer;
	protected Layer previousLayer;
	
	protected Layer(List<Neuron> neurons){
		this.neurons = neurons;
	}
	
	protected Layer(int neuronsCount, ActivationFunction transferFunction) {
		this.neurons = new ArrayList<Neuron>();
		for(int i = 0; i < neuronsCount; i++){
			neurons.add(new Neuron(transferFunction));
		}
	}

	public void connect(Layer nextLayer, boolean initializeNeurons){
		this.nextLayer = nextLayer;
		nextLayer.previousLayer = this;
		if (initializeNeurons)
			initializeNeurons();
	}
	
	private void initializeNeurons(){
		for(Neuron neuron : nextLayer.neurons){
			neuron.connect(getNeuronCount());
		}
	}
	
	public int getNeuronCount(){
		return neurons.size();
	}
	
	public List<Double> compute(List<Double> inputs){
		List<Double> outputs = computeOutputs(inputs);
		return nextLayer.compute(outputs);
	}

	protected List<Double> computeOutputs(List<Double> inputs) {
		List<Double> outputs = new ArrayList<Double>();
		for(int i = 0; i < neurons.size(); i++){
			outputs.add(neurons.get(i).compute(inputs));
		}
		return outputs;
	}
	
	public void train() {
		for(int thisLayer = 0; thisLayer < neurons.size(); thisLayer++){
			for(int i = 0; i < previousLayer.neurons.size(); i++){
				Neuron previous = previousLayer.neurons.get(i);
				previous.increaseSigma(neurons.get(thisLayer).getSigma() * neurons.get(thisLayer).getWeight(i));
			}
		}
		previousLayer.train();
	}
	
	public void updateWeights() {
		for(Neuron neuron : neurons){
			neuron.updateWeights();
		}
		nextLayer.updateWeights();
	}
	
	@Override
	public String toString(){
		StringBuilder sb = new StringBuilder();
		sb.append(": ").append(getNeuronCount()).append(" neurons.\n").append("Neurons:\n");
		for(Neuron n : neurons){
			sb.append(n.toString());
		}
		return sb.toString() + "Next layer:\n" + nextLayer.toString();
	}
}
